package procilege.impl;

import java.io.StringReader;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.xml.namespace.QName;

import merisis.MerisisVM;
import merisis.sense.SenseMaker;
import merisis.traitspace.EntityHappen;
import merisis.traitspace.Morphism;
import procilege.compiler.PalCompiler;
import procilege.lang.Name;
import procilege.lang.PrivExprElement;
import procilege.lang.PrivilegeForm;

import com.google.inject.Inject;

public class ComputationalEntityMgr {

	public final static String PAL_METAENTITY_NS = "urn:procilege/pal/meta-entities/";
	public final static String PAL_LITERAL_CONTENT = "literal-content";
	public final static String PAL_PRIVILEGE_EXPR = "privilege-expr";
	public final static String PAL_PNORMAL_EXPR = "pnormal-expr";
	public final static String PROPS_NS = "urn:procilege/props/";
	public final static String PROPS_ALLENTITYS = "AllEntities";
	public final static String RELATIONS_ISA = "isa";

	@Inject
	private SenseMaker smr;

	@Inject
	private MerisisVM mvm;

	@Inject
	private PalCompiler pal;

	@Inject
	private Logger logger;

	@Inject
	private Morphism mp;

	@Inject
	private EntityHappen eh;

	private long namePalLiteral = 0;

	private long namePrivExpr = 0;

	private long nameNormalForm = 0;

	private long namePropEntities = 0;

	private long nameIsA = 0;

	private boolean initialized = true;

	public ComputationalEntityMgr() {

	}

	private void init() {
		
		if (!initialized)
			return;

		namePalLiteral = smr.sense(new QName(PAL_METAENTITY_NS,
				PAL_LITERAL_CONTENT));
		namePrivExpr = smr.sense(new QName(PAL_METAENTITY_NS,
				PAL_PRIVILEGE_EXPR));
		nameNormalForm = smr.sense(new QName(PAL_METAENTITY_NS,
				PAL_PNORMAL_EXPR));

		namePropEntities = smr.sense(new QName(PROPS_NS, PROPS_ALLENTITYS));
		
		nameIsA = smr.sense(new QName(PROPS_NS, RELATIONS_ISA));
		
		eh.ripple(new long[] {namePrivExpr}, PrivilegeFormReaction.class);
		
		initialized = false;
	}

	public long updateContentOfPalLiteral(long threadfocus, String literal) {

		if (initialized)
			init();

		return 0;
	}

	public void updateLiteralContent(String content) {

		if (initialized)
			init();

		if (logger.isLoggable(Level.FINEST)) {
			StringBuilder sb = new StringBuilder();
			sb.append("content:\n");
			sb.append(content);
			logger.finest(sb.toString());
		}

		pal.compile(null, new StringReader(content));
		// update(name, namePrivExpr, expr);
	}

	public void updatePrivlegeExpr(Name name, PrivExprElement expr) {

		if (initialized)
			init();

		mvm.give(((NameImpl) name).nameId);
		mvm.stepin();
		try {
			long[] ontos = new long[1];
			ontos[0] = namePrivExpr;
			eh.happen(ontos, expr);
		} finally {
			mvm.stepout();
		}
	}

	public PrivExprElement retrievePrivlegeExpr(Name name) {

		if (initialized)
			init();

		Object value;

		mvm.give(((NameImpl) name).nameId);
		mvm.stepin();
		try {
			value = eh.retrieve(new long[] { namePrivExpr });
		} finally {
			mvm.stepout();
		}

		if (value instanceof PrivExprElement) {
			return (PrivExprElement) value;
		} else {
			logger.warning("invalid type: " + value);
			return null;
		}
	}

	public PrivilegeForm retrievePrivlegeForm(Name name) {

		if (initialized)
			init();

		if (initialized)
			init();

		Object value;

		mvm.give(((NameImpl) name).nameId);
		mvm.stepin();
		try {
			value = eh.retrieve(new long[] { nameNormalForm });
		} finally {
			mvm.stepout();
		}

		if (value instanceof PrivilegeForm) {
			return (PrivilegeForm) value;
		} else {
			logger.warning("invalid type: " + value);
			return null;
		}
	}

	public void setEntityProperty(Name entity, String property, Object value) {

		if (initialized)
			init();

		mvm.give(((NameImpl) entity).nameId);
		mvm.stepin();
		try {
			long[] ontos = new long[2];
			ontos[0] = smr.sense(new QName(PROPS_NS, property));
			ontos[1] = namePropEntities;
			eh.happen(ontos, value);
		} finally {
			mvm.stepout();
		}
	}

	public Object getEntityProperty(Name entity, String property) {

		if (initialized)
			init();

		Object value;

		mvm.give(((NameImpl) entity).nameId);
		mvm.stepin();
		try {
			long[] ontos = new long[2];
			ontos[0] = smr.sense(new QName(PROPS_NS, property));
			ontos[1] = namePropEntities;
			value = eh.retrieve(ontos);
		} finally {
			mvm.stepout();
		}

		return value;
	}

	public void setIsA(Name subject, Name object) {

		if (initialized)
			init();

		long sid = ((NameImpl) subject).nameId;
		long oid = ((NameImpl) object).nameId;

		mp.set(nameIsA, sid, oid);
	}

	public void unsetIsA(Name subject, Name object) {

		if (initialized)
			init();

		throw new UnsupportedOperationException();
	}

	public boolean isa(Name subject, Name object) {

		if (initialized)
			init();

		long sid = ((NameImpl) subject).nameId;
		long oid = ((NameImpl) object).nameId;

		return mp.exists(nameIsA, sid, oid);
	}
	
	public boolean equiv(long[][] a, long[][] b) {

		if (initialized)
			init();

		if (!mp.proof(nameIsA, a, b))
			return false;

		if (!mp.proof(nameIsA, b, a))
			return false;
		
		return true;
	}
}